#include "Tracer.h"
#include "StrUtil.h"
#include "ArgUtil.h"
#include "Thread.h"
#include "DateTime.h"
#include <unistd.h>
#include <iostream>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


using namespace libemb;

/* 这是自己设计的一个测试内核调度实时性的工具,类似于cyclictest,但比cyclictest更简单,更容易理解. */
static int s_interval = 0;
static int s_cycle = 0;
static bool s_debug = false;
static volatile int s_finished = 0;

class Cyclic:public Runnable{
public:
	Cyclic(int threadID)
	{
		m_totalCycle = 0;
		m_threadID = threadID;
	}
	void run()
	{
		while(m_totalCycle<=s_cycle)
		{
			m_totalCycle++;
			m_currTime=Time::currentTimeMonotonic();/* 记录线程投入运行的时间 */
			if(m_totalCycle!=1)
			{
				/* 计算从上次休眠到现在的时间间隔,即为CPU重新调度到本线程的时间
                 * 我们通过观察这个值，与我们设定的期望值(休眠时间)比较,就可以知道内核调度器的实时性能.
				 */
				m_diffTime = m_currTime - m_lastTime;
				if(m_totalCycle==2)
				{
					m_minTime = m_diffTime;
					m_maxTime = m_diffTime;
				}
				else
				{
					Time tmp = m_diffTime-m_minTime;
					if (tmp.secondPart()<0)
					{
						m_minTime = m_diffTime;
					}
					tmp = m_maxTime - m_diffTime;
					if (tmp.secondPart() < 0)
					{
						m_maxTime = m_diffTime;
					}
				}
			}
			
			m_totalTime = m_totalTime + m_diffTime;
			if (s_debug)
			{
				TRACE_GREEN("[Thread%4d:%-5d %lldus]\n",m_threadID,m_totalCycle,m_diffTime.toMicroSeconds());
			}
			m_lastTime = Time::currentTimeMonotonic();/* 记录线程睡眠之前的时间 */
			Thread::usleep(s_interval);/* 睡眠一定的时间,线程期望在睡眠这个时间间隔后,调度器能重新把CPU分给自己 */
		}

		
		long long total = m_totalTime.toMicroSeconds();
		int avg = total / (m_totalCycle-1);
		TRACE_YELLOW("Thread[%4d] cycles:%5d, min:%6lldus, max:%6lldus, avg:%6dus\n",m_threadID,m_totalCycle-1,
			          m_minTime.toMicroSeconds(),m_maxTime.toMicroSeconds(),avg);
		s_finished++;
	}

private:
	Time m_currTime,m_lastTime,m_diffTime;
	Time m_totalTime,m_maxTime,m_minTime;
	int m_totalCycle;
	int m_threadID;
};

int main(int argc, char* argv[])
{
	std::string argValue;
	ArgOption  argOption;
	Thread scmServiceThread;
	Time startTime = Time::currentTimeMonotonic();
	/* 处理参数 */
	argOption.addOption("h", 0);	/* -h:帮助 */
	argOption.addOption("d", 0);	/* -d:打开调试 */
	argOption.addOption("n", 1);	/* -n:线程数量 */
	argOption.addOption("i", 1);	/* -i:间隔时间(us) */
	argOption.addOption("c", 1);	/* -c:循环次数 */
	argOption.addOption("p", 1);	/* -p:调度策略 */
	argOption.addOption("l", 1);	/* -l:打印级别 */
	
	argOption.parseArgs(argc,argv);	/* 解析参数 */
	if(argOption.getValue("h", argValue))
	{
		FilePath filePath(argv[0]);
		TRACE_YELLOW("schedtest help>>>\n");
		TRACE_YELLOW("%s [-h] [-n threadnum -i interval(us) -c cycles -p policy]\n",CSTR(filePath.baseName()));
		TRACE_YELLOW("         -n thread number\n");
		TRACE_YELLOW("         -i interval time(us)\n");
		TRACE_YELLOW("         -c run cycles\n");
		TRACE_YELLOW("         -p policy(0:SCHED_OTHER,1:SCHED_FIFO,2:SCHED_RR)\n");
		return RC_OK;
	}

	if (argOption.getValue("l", argValue) && !argValue.empty())
	{
		Tracer::getInstance().setLevel(StrUtil::stringToInt(argValue));
	}
	else
	{
		/* 默认打印等级为REL */
		Tracer::getInstance().setLevel(TRACE_LEVEL_REL);
	}

	int threadNums;
	if (!argOption.getValue("n", argValue))
	{
		TRACE_RED("need threadnum(-n), use -h for help!\n");
		return RC_ERROR;
	}
	else
	{
		threadNums = StrUtil::stringToInt(argValue);
		threadNums = MAX(1,threadNums);
	}

	if (!argOption.getValue("i", argValue))
	{
		TRACE_RED("need interval(-i), use -h for help!\n");
		return RC_ERROR;
	}
	else
	{
		s_interval = StrUtil::stringToInt(argValue);
		s_interval = MAX(1,s_interval);
	}

	if (!argOption.getValue("c", argValue))
	{
		TRACE_RED("need cycles(-c), use -h for help!\n");
		return RC_ERROR;
	}
	else
	{
		s_cycle = StrUtil::stringToInt(argValue);
		s_cycle = MAX(1,s_cycle);
	}

	if (argOption.getValue("d", argValue))
	{
		s_debug = true;
	}

	int policy;
	if (!argOption.getValue("p", argValue))
	{
		TRACE_RED("need policy(-p), use -h for help!\n");
		return RC_ERROR;
	}
	else
	{
		policy = StrUtil::stringToInt(argValue);
		policy = CLIP(0,policy,2);
	}

	/* 初始化实时抢占 */
	Thread* threadArray[threadNums];
	Cyclic* runnableArray[threadNums];
	
	for (int i=0; i<threadNums; i++)
	{
		runnableArray[i] = new Cyclic(i);
		threadArray[i] = new Thread;
		switch(policy){
		case 1:
		case 2:
		{
			threadArray[i]->setAttribute(policy,80);
			break;
		}
		default:
			break;
		}
	}

	for (int i=0; i<threadNums; i++)
	{
		threadArray[i]->start(*runnableArray[i]);
	}
	
	while(1)
	{
		Thread::msleep(1000);
		//TRACE_GREEN("%s is running  @ %s.\n",argv[0],CSTR(DateTime::currentDateTime().toString()));
		if(s_finished==threadNums)
		{
			TRACE_GREEN("%s is finished @ %s.\n",argv[0],CSTR(DateTime::currentDateTime().toString()));
			break;
		}
	}
	
    return 0;
}
